TransMAP Hub
Vessel Traffic Example¶
Using moving pandas with the TransMAP REST API
In [2]:
# Source ref: https://github.com/anitagraser/movingpandas-examples/blob/main/analysis-examples/2-ship-data.ipynb
make_url = lambda uri: "https://oak.cast.uark.edu/{0}".format(uri)
In [1]:
%matplotlib inline
In [3]:
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))
In [4]:
import requests
import numpy as np
import pandas as pd
import geopandas
from shapely.geometry import Point, LineString, Polygon
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import movingpandas as mpd
import warnings
warnings.simplefilter("ignore")
In [5]:
mpd.__version__
def make_date(year: int, month: int, day: int, hour: int = 0, minute: int = 0, second: int = 0):
return datetime(year, month, day, hour, minute, second).strftime("%Y-%m-%dT%H:%M:%S")
In [12]:
ais_data = requests.get(make_url('ais/vessels'), params={
"limit": 50000,
"start_date": make_date(2020, 5, 29),
"end_date": make_date(2020, 5, 30)
})
ais_data.raise_for_status()
In [13]:
df = pd.DataFrame(ais_data.json())
df
Out[13]:
| MMSI | BaseDateTime | LAT | LON | SOG | COG | Heading | VesselName | IMO | CallSign | VesselType | Status | Length | Width | Draft | Cargo | TranscieverClass | location | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 369330000 | 2020-05-29T00:00:00 | 29.13493 | -90.20348 | 0.0 | 180.4 | 339.0 | None | None | None | None | 5.0 | None | None | None | None | A | [-90.20348, 29.13493] |
| 1 | 368076640 | 2020-05-29T00:00:00 | 29.60997 | -84.58777 | 0.0 | 30.4 | 511.0 | None | None | None | None | 0.0 | None | None | None | None | B | [-84.58777, 29.60997] |
| 2 | 368043520 | 2020-05-29T00:00:00 | 27.88024 | -97.27268 | 0.0 | -80.6 | 244.0 | None | None | None | None | 0.0 | None | None | None | None | B | [-97.27268, 27.88024] |
| 3 | 367314740 | 2020-05-29T00:00:00 | 31.14192 | -92.26107 | 1.2 | -87.0 | 511.0 | None | None | None | None | 15.0 | None | None | None | None | B | [-92.26107, 31.14192] |
| 4 | 368926530 | 2020-05-29T00:00:00 | 29.82006 | -91.54392 | 0.0 | 86.9 | 317.0 | None | None | None | None | 5.0 | None | None | None | None | A | [-91.54392, 29.82006] |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 49995 | 367653590 | 2020-05-29T00:07:49 | 38.28540 | -122.28518 | 1.1 | 44.6 | 511.0 | NOMATA | None | WDH7990 | 37.0 | None | 18.0 | 5.0 | None | None | B | [-122.28518, 38.2854] |
| 49996 | 366926740 | 2020-05-29T00:07:49 | 33.75942 | -118.27430 | 0.4 | 21.7 | 102.0 | TIM QUIGG | IMO8987917 | WDB6570 | 31.0 | 0.0 | 24.0 | 9.0 | 4.0 | 52.0 | B | [-118.2743, 33.75942] |
| 49997 | 366978690 | 2020-05-29T00:07:49 | 37.71104 | -122.80402 | 6.7 | -172.6 | 240.0 | EAGLE | IMO8661654 | WBP2187 | 31.0 | 0.0 | 29.0 | 9.0 | None | None | A | [-122.80402, 37.71104] |
| 49998 | 368011470 | 2020-05-29T00:07:49 | 29.62379 | -89.90540 | 1.9 | 144.2 | 511.0 | LOUISE | None | WDJ7929 | 31.0 | 0.0 | 17.0 | 7.0 | None | 31.0 | B | [-89.9054, 29.62379] |
| 49999 | 366878290 | 2020-05-29T00:07:49 | 39.27215 | -74.98030 | 0.0 | -153.3 | 511.0 | RAM XV | IMO8767305 | WDB2653 | 90.0 | 15.0 | 33.0 | 19.0 | 2.1 | None | B | [-74.9803, 39.27215] |
50000 rows × 18 columns
In [14]:
df2 = pd.DataFrame(df.location.tolist(), index= df.index)
gpdf = geopandas.GeoDataFrame(df, geometry=geopandas.points_from_xy(df2[0], df2[1]))
In [15]:
gpdf.set_crs('epsg:4326')
Out[15]:
| MMSI | BaseDateTime | LAT | LON | SOG | COG | Heading | VesselName | IMO | CallSign | VesselType | Status | Length | Width | Draft | Cargo | TranscieverClass | location | geometry | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 369330000 | 2020-05-29T00:00:00 | 29.13493 | -90.20348 | 0.0 | 180.4 | 339.0 | None | None | None | None | 5.0 | None | None | None | None | A | [-90.20348, 29.13493] | POINT (-90.20348 29.13493) |
| 1 | 368076640 | 2020-05-29T00:00:00 | 29.60997 | -84.58777 | 0.0 | 30.4 | 511.0 | None | None | None | None | 0.0 | None | None | None | None | B | [-84.58777, 29.60997] | POINT (-84.58777 29.60997) |
| 2 | 368043520 | 2020-05-29T00:00:00 | 27.88024 | -97.27268 | 0.0 | -80.6 | 244.0 | None | None | None | None | 0.0 | None | None | None | None | B | [-97.27268, 27.88024] | POINT (-97.27268 27.88024) |
| 3 | 367314740 | 2020-05-29T00:00:00 | 31.14192 | -92.26107 | 1.2 | -87.0 | 511.0 | None | None | None | None | 15.0 | None | None | None | None | B | [-92.26107, 31.14192] | POINT (-92.26107 31.14192) |
| 4 | 368926530 | 2020-05-29T00:00:00 | 29.82006 | -91.54392 | 0.0 | 86.9 | 317.0 | None | None | None | None | 5.0 | None | None | None | None | A | [-91.54392, 29.82006] | POINT (-91.54392 29.82006) |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 49995 | 367653590 | 2020-05-29T00:07:49 | 38.28540 | -122.28518 | 1.1 | 44.6 | 511.0 | NOMATA | None | WDH7990 | 37.0 | None | 18.0 | 5.0 | None | None | B | [-122.28518, 38.2854] | POINT (-122.28518 38.28540) |
| 49996 | 366926740 | 2020-05-29T00:07:49 | 33.75942 | -118.27430 | 0.4 | 21.7 | 102.0 | TIM QUIGG | IMO8987917 | WDB6570 | 31.0 | 0.0 | 24.0 | 9.0 | 4.0 | 52.0 | B | [-118.2743, 33.75942] | POINT (-118.27430 33.75942) |
| 49997 | 366978690 | 2020-05-29T00:07:49 | 37.71104 | -122.80402 | 6.7 | -172.6 | 240.0 | EAGLE | IMO8661654 | WBP2187 | 31.0 | 0.0 | 29.0 | 9.0 | None | None | A | [-122.80402, 37.71104] | POINT (-122.80402 37.71104) |
| 49998 | 368011470 | 2020-05-29T00:07:49 | 29.62379 | -89.90540 | 1.9 | 144.2 | 511.0 | LOUISE | None | WDJ7929 | 31.0 | 0.0 | 17.0 | 7.0 | None | 31.0 | B | [-89.9054, 29.62379] | POINT (-89.90540 29.62379) |
| 49999 | 366878290 | 2020-05-29T00:07:49 | 39.27215 | -74.98030 | 0.0 | -153.3 | 511.0 | RAM XV | IMO8767305 | WDB2653 | 90.0 | 15.0 | 33.0 | 19.0 | 2.1 | None | B | [-74.9803, 39.27215] | POINT (-74.98030 39.27215) |
50000 rows × 19 columns
In [16]:
gpdf.plot()
Out[16]:
<AxesSubplot:>
In [17]:
gpdf['t'] = pd.to_datetime(gpdf['BaseDateTime'], format='%Y-%m-%dT%H:%M:%S')
In [18]:
gpdf = gpdf.set_index('t')
In [19]:
gpdf['SOG'].hist(bins=10, figsize=(15, 3))
Out[19]:
<AxesSubplot:>
In [20]:
print("Original size: {} rows".format(len(gpdf)))
gpdf = gpdf[gpdf.SOG>0]
print("Reduced to {} rows after removing 0 speed records".format(len(gpdf)))
gpdf['SOG'].hist(bins=10, figsize=(15,3))
Original size: 50000 rows Reduced to 20940 rows after removing 0 speed records
Out[20]:
<AxesSubplot:>
In [21]:
gpdf['VesselType'] = gpdf['VesselType'].astype(float)
gpdf['VesselType'].value_counts().plot(kind='bar', figsize=(15,3))
Out[21]:
<AxesSubplot:>
In [22]:
%%time
MIN_LENGTH = 0.02 # degrees - May need to reproject to use meters
traj_collection = mpd.TrajectoryCollection(gpdf, 'MMSI', min_length=MIN_LENGTH)
print("Finished creating {} trajectories".format(len(traj_collection)))
Finished creating 550 trajectories Wall time: 15.5 s
In [23]:
traj_collection = mpd.MinTimeDeltaGeneralizer(traj_collection).generalize(tolerance=timedelta(minutes=1))
# this is looking for a specific 't' index
In [24]:
shiptype_to_color = {'Passenger': 'blue', 'HSC': 'green', 'Tanker': 'red', 'Cargo': 'orange'}
# Should be changed to map to these codes: https://coast.noaa.gov/data/marinecadastre/ais/VesselTypeCodes2018.pdf
traj_collection.plot(column='VesselType', column_to_color=shiptype_to_color, linewidth=1, capstyle='round')
Out[24]:
<AxesSubplot:>
In [25]:
passenger = traj_collection
passenger.hvplot(title='Passenger ferries', line_width=2)
Out[25]:
In [ ]:
In [ ]:
Last update: 2021-05-04